#include "KeepTable.h"

#include "EventFileDriver.h"
#include "EventHash.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

typedef struct _KeepItem
{
	time_t	last_logon;
	time_t	keep_time;
	euint64 online_id;
	euint32 user_id;
	bool	is_online;
} KeepItem;

struct _KeepTable
{
	EventFileDriver parent_instance;
	EventHash      *k_table;
};

static void
keep_table_finalize(KeepTable *self)
{
	/* 调用父类析构 */
	event_file_driver_finalize(EVENT_FILE_DRIVER(self));
	event_object_unref(self->k_table);
}

static int
keep_table_parser(EventFileDriver *driver, const epointer data, int length)
{
	KeepTable *self = KEEP_TABLE(driver);
	KeepItem  *item;

	return_val_if_fail(driver != NULL, keep_table_parser, 1);
	return_val_if_fail(length == sizeof(KeepItem), keep_table_parser, 1);

	item = MALLOC_NEW(KeepItem);
	memcpy(item, data, length);
	event_hash_set(self->k_table, EPTR(item->online_id), item, free);

	return 0;
}

static EventFileDriverPack *
keep_table_writer(EventFileDriver *driver)
{
	KeepTable *self = KEEP_TABLE(driver);
	KeepItem  *item;
	HashItem_p h_item;

	return_val_if_fail(driver != NULL, keep_table_writer, NULL);

	h_item = event_hash_iter_get(self->k_table);
	if (!h_item)
		return NULL;
	item = (KeepItem *)event_hash_item_get_value(h_item);
	return event_file_driver_pack_new_from_raw(
		event_raw_new_with_stream(sizeof(KeepItem), item));
}

static int
keep_table_prepare_data(EventFileDriver *driver)
{
	KeepTable *self = KEEP_TABLE(driver);

	return_val_if_fail(self != NULL, keep_table_prepare_data,
			   EVENT_FILE_DRIVER_PREPARE_DATA_ERROR);

	event_hash_iter_reset(self->k_table);
	event_file_driver_flush_items_num(
		driver, (eint32)event_hash_get_used_size(self->k_table));
	return EVENT_FILE_DRIVER_SUCCESS;
}

KeepTable *
keep_table_new(void)
{
	KeepTable *object = MALLOC_NEW(KeepTable);

	event_file_driver_init(EVENT_FILE_DRIVER(object));
	event_object_set_finalize(object, FINALIZE(keep_table_finalize));

	event_file_driver_set_parser(EVENT_FILE_DRIVER(object),
				     keep_table_parser);
	event_file_driver_set_writer(EVENT_FILE_DRIVER(object),
				     keep_table_writer,
				     keep_table_prepare_data);
	event_file_driver_set_file_not_exist_throw(EVENT_FILE_DRIVER(object),
						   false);

	object->k_table = event_hash_new(EVENT_HASH_MODEL_NUMBER);

	return object;
}

euint32
keep_table_get_uid_from_oid(KeepTable *self, euint64 oid)
{
	KeepItem *k_item;
	time_t	  now_time;
	return_val_if_fail(self != NULL, keep_table_get_uid_from_oid, 0);

	k_item = (KeepItem *)event_hash_get(self->k_table, EPTR(oid));
	if (!k_item || !k_item->is_online)
		return 0;
	/* 保持时间超过 5 分钟则置为不在线，且无法取得 UserID */
	now_time = time(NULL);
	if ((now_time - k_item->keep_time) > 300L) {
		k_item->is_online = false;
		return 0;
	}
	return k_item->user_id;
}

bool
keep_table_verify_existed_online_id(KeepTable *self, euint64 oid)
{
	KeepItem *k_item;
	return_val_if_fail(self != NULL, keep_table_verify_existed_online_id,
			   false);
	k_item = event_hash_get(self->k_table, EPTR(oid));
	return k_item != NULL;
}

int
keep_table_verify_logon_state(KeepTable *self, euint64 oid)
{
	KeepItem *k_item;
	time_t	  now_time;
	return_val_if_fail(self != NULL, keep_table_verify_logon_state, 2);

	k_item = event_hash_get(self->k_table, EPTR(oid));
	if (!k_item)
		return 2;

	now_time = time(NULL);
	/* 超过 30 天需要验证码登陆 */
	if ((now_time - k_item->last_logon) > 2592000L)
		return 2;
	/* 10 天内无需验证码登陆 */
	if ((now_time - k_item->last_logon) > 864000L)
		return 1;
	/* 在线状态保持有效期 10 分钟 */
	if ((now_time - k_item->keep_time) > 600L)
		return 1;
	else
		return 0;
}

int
keep_table_flush_online_state(KeepTable *self, euint64 online_id,
			      bool only_flush_ktime)
{
	KeepItem *k_item;
	time_t	  now_time;
	return_val_if_fail(self, keep_table_flush_online_state, 2);

	k_item = (KeepItem *)event_hash_get(self->k_table, EPTR(online_id));

	if (!k_item)
		return 1;
	now_time	  = time(NULL);
	k_item->keep_time = now_time;
	if (!only_flush_ktime) {
		k_item->last_logon = now_time;
		k_item->is_online  = true;
	}
	return 0;
}

void
keep_table_add_online_item(KeepTable *self, euint64 online_id, euint32 user_id)
{
	KeepItem *k_item;
	time_t	  now_time;
	return_if_fail(self != NULL, keep_table_add_online_item);

	if (keep_table_verify_existed_online_id(self, online_id))
		return;
	now_time	   = time(NULL);
	k_item		   = MALLOC_NEW(KeepItem);
	k_item->is_online  = true;
	k_item->keep_time  = now_time;
	k_item->last_logon = now_time;
	k_item->online_id  = online_id;
	k_item->user_id	   = user_id;
	event_hash_set(self->k_table, EPTR(online_id), k_item, free);
}

void
keep_table_remove_online_item(KeepTable *self, euint64 online_id)
{
	return_if_fail(self != NULL, keep_table_remove_online_item);

	if (!keep_table_verify_existed_online_id(self, online_id))
		return;
	event_hash_remove(self->k_table, EPTR(online_id));
}

void
__keep_table_add_log(KeepTable *self, euint64 online_id, euint32 user_id)
{
	KeepItem *item;
	return_if_fail(self != NULL, __keep_table_add_log);

	item = (KeepItem *)event_hash_get(self->k_table, EPTR(online_id));
	if (item) {
		item->is_online	 = true;
		item->keep_time	 = time(NULL);
		item->last_logon = time(NULL);
		return;
	}

	item = MALLOC_NEW(KeepItem);
	//
	item->is_online	 = true;
	item->online_id	 = online_id;
	item->user_id	 = user_id;
	item->keep_time	 = time(NULL);
	item->last_logon = time(NULL);
	event_hash_set(self->k_table, EPTR(item->online_id), item, free);
	event_file_driver_add_count_once(EVENT_FILE_DRIVER(self));
}

void
__keep_table_print(KeepTable *self)
{
	KeepItem  *item;
	HashItem_p h_item;
	return_if_fail(self != NULL, __keep_table_print);

	event_hash_iter_reset(self->k_table);

	h_item = event_hash_iter_get(self->k_table);
	item   = h_item ? (KeepItem *)event_hash_item_get_value(h_item) : NULL;
	printf("UserID\tonlineID\tIs it online\tLast logon\n");
	while (item) {
		printf("%u\t%lu\t%s\t%ld\n", item->user_id, item->online_id,
		       (item->is_online ? "Yes" : " No"), item->last_logon);
		h_item = event_hash_iter_get(self->k_table);
		item = h_item ? (KeepItem *)event_hash_item_get_value(h_item) :
				NULL;
	}
}
